package com.email.service.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Authenticator;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.URLName;

import com.email.config.BasicEmailServerConfig;
import com.email.core.Caller;
import com.email.core.CallerWithArgs;
import com.email.utils.*;
import com.email.view.TipDialog;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import com.sun.mail.util.MailSSLSocketFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.*;

import com.email.service.IMail;



public class EmailSenderAndReceiver implements IMail {
    public Logger logger = null;
//    private String host;
    private String port;
    private String from;
    private String to;
    private String title;
    private String content;
    private List<File> attachmentlist = new ArrayList<File>();
    private String username;
    private String password;
    private String sendEmployeeId;

    private BasicEmailServerConfig serverConfig;

    private Map<String, Object> parameter = new HashMap<String, Object>();

//    public String getHost() {
//        return host;
//    }

//    public void setHost(String host) {
//        this.host = host;
//    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public List<File> getAttachmentlist() {
        return attachmentlist;
    }

    public void setAttachmentlist(List<File> attachmentlist) {
        this.attachmentlist = attachmentlist;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSendEmployeeId() {
        return sendEmployeeId;
    }

    public void setSendEmployeeId(String sendEmployeeId) {
        this.sendEmployeeId = sendEmployeeId;
    }

    public EmailSenderAndReceiver(String from, String to, String title, String content, List<File> attachmentlist,
                       String username, String password, String port) {
    	
        // 获得当前目录路径
        String filePath = this.getClass().getResource("/").getPath();
        // 找到log4j.properties配置文件所在的目录(已经创建好)
        filePath = filePath.substring(1).replace("bin", "src");
        // 获得日志类logger的实例
        logger = Logger.getLogger(EmailSenderAndReceiver.class);
        // logger所需的配置文件路径
        // PropertyConfigurator.configure(filePath+"log4j.properties");
//        this.host = host;
        this.from = from;
        this.to = to;
        this.title = title;
        this.content = content;
        this.attachmentlist = attachmentlist;
        this.username = username;
        this.password = password;
        this.port = port;

        this.serverConfig = new BasicEmailServerConfig(CommonUtil.getEmailHostName(this.from));
    }

    public EmailSenderAndReceiver(String to, String title, String content, List attachmentlist) {
        this.to = to;
        this.title = title;
        this.content = content;
        this.attachmentlist = attachmentlist;

        this.serverConfig = new BasicEmailServerConfig();
    }

    public EmailSenderAndReceiver(String to, String title, String content) {
        this.to = to;
        this.title = title;
        this.content = content;

        this.serverConfig = new BasicEmailServerConfig();
    }

    public void addAttachments(File file) {
        attachmentlist.add(file);
    }

    public String[] sendMail() {
        String[] result = new String[2];
        Session session = null;
        Properties props = System.getProperties();
        props.put("mail.smtp.host", serverConfig.getSmtpServer());
        props.put("mail.smtp.sendpartial", "true");
        props.put("mail.smtp.prot", port);

        System.out.println("==> SSL Config: " + serverConfig.needSSL());

        if (serverConfig.needSSL()) {
            try {
                MailSSLSocketFactory sf = new MailSSLSocketFactory();
                sf.setTrustAllHosts(true);
                props.put("mail.smtp.ssl.enable", "true");
                props.put("mail.smtp.ssl.socketFactory", sf);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("==> Need SSL Connection");
        }

        if (StringUtils.isBlank(username)) {
            session = Session.getDefaultInstance(props, null);
        } else {
            props.put("mail.smtp.auth", "true");
            EmailAuthenticator auth = new EmailAuthenticator(username, password);
            session = Session.getInstance(props, auth);
        }

        try {
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            InternetAddress[] toAddr = InternetAddress.parse(to);
            message.addRecipients(MimeMessage.RecipientType.CC, InternetAddress.parse(from));
            message.addRecipients(Message.RecipientType.TO, toAddr);
            message.setSubject(title);
            message.setSentDate(new Date());
            Multipart multipart = new MimeMultipart();
            MimeBodyPart contentPart = new MimeBodyPart();
            contentPart.setText(content);
            multipart.addBodyPart(contentPart);

            if (attachmentlist != null && attachmentlist.size() > 0) {
                for (int i = 0; i < attachmentlist.size(); i++) {
                    MimeBodyPart attachmentPart = new MimeBodyPart();

                    FileDataSource source = new FileDataSource(attachmentlist.get(i));
                    attachmentPart.setDataHandler(new DataHandler(source));
                    attachmentPart.setFileName(MimeUtility.encodeWord(attachmentlist.get(i).getName(), "gb2312", null));
                    multipart.addBodyPart(attachmentPart);
                }
            }
            message.setContent(multipart);

            if (message.getAllRecipients() == null) {
                result[0] = "1";
                result[1] = "请设置收件人邮箱";
            } else {
                if (StringUtils.isBlank(username)) {
                    Transport.send(message);
                } else {
                    try {
                        Transport transport = session.getTransport("smtp");
                        transport.connect();
                        transport.sendMessage(message, message.getAllRecipients());

                        result[0] = "0";
                        result[1] = "发送成功";

                        logger.info("邮件发送成功!发送人: " + from);

                        transport.close();
                    } catch (MessagingException mex) {
                        result[0] = "1";
                        result[1] = "邮件服务器发生错误";

                        if (mex instanceof AuthenticationFailedException) {
                            result[1] = "用户名或密码错误";
                        }

                        logger.info("==> 邮件发送失败：" + result[1]);
                    } catch (Exception e) {
                        result[0] = "1";
                        result[1] = "系统异常";

                        logger.info("==> 邮件发送失败：" + result[1]);
                    }
                }
            }
        } catch (MessagingException mex) {
            result[0] = "1";
            result[1] = "邮件服务器发生错误";

            if (mex instanceof AuthenticationFailedException) {
                result[1] = "用户名或密码错误";
            }

            logger.info("==> 邮件发送失败：" + result[1]);
        } catch (Exception e) {
            result[0] = "1";
            result[1] = "系统异常";

            logger.info("==> 邮件发送失败：" + result[1]);
        }
        return result;
    }

    public String[] sendFile(File file) {
        if (attachmentlist == null) {
            attachmentlist = new ArrayList<File>();
        }
        attachmentlist.clear();
        attachmentlist.add(file);
        return sendMail();
    }

    public void receiveMail(CallerWithArgs caller, Caller finishCaller) {
        try {
            Properties props = new Properties();
            props.setProperty("mail.store.protocol", "imap");
            props.setProperty("mail.imap.host", serverConfig.getSmtpServer());

            System.out.println("==> SSL Config: " + serverConfig.needSSL());

            if (serverConfig.needSSL()) {
                System.out.println("==> Need SSL Connection");

                // 按需要更改
                props.setProperty("mail.imap.port", serverConfig.getPort() + "");
                // SSL安全连接参数
                props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                props.setProperty("mail.imap.socketFactory.fallback", "true");
                props.setProperty("mail.imap.socketFactory.port", serverConfig.getPort() + "");

//                MailSSLSocketFactory sf = new MailSSLSocketFactory();
//                sf.setTrustAllHosts(true);
//                props.put("mail.smtp.ssl.enable", "true");
//                props.put("mail.smtp.ssl.socketFactory", sf);
            }

            Session session = Session.getDefaultInstance(props);

            URLName urlname = new URLName(serverConfig.getProtocol(), serverConfig.getPopServer(),
                    serverConfig.getPort(), null, this.from, this.password);
            System.out.println("==> Receive email: " + urlname);
            IMAPStore store = (IMAPStore)session.getStore("imap");
            store.connect(serverConfig.getPopServer(), 993, this.from, this.password);
            IMAPFolder folder =  (IMAPFolder)store.getDefaultFolder();
            if (folder == null) {
                System.out.println("the server is not available");
                return;
            }

            Folder popFolder = folder.getFolder("INBOX");
            popFolder.open(Folder.READ_WRITE);
            Message[] messages = popFolder.getMessages();
            int msgCount = popFolder.getMessageCount();
            System.out.println("==> Total " + msgCount + " emails.");
            for (int i = 0; i < msgCount; i++) {
                // 第一次调用，显示进度0
                caller.call(0);
                String content = mailReceiver(i, messages[i]);
                String html = HTMLFilter.htmlSpecialChars(parameter.get("c") != null ? parameter.get("c").toString() : "无标题");
                html = SQLFilter.sqlInject(html);
                String emailPath = "emails/" + TxtUtils.StringFilter(html) + "-" + i + "-" + parameter.get("h") + ".html";
                File file = new File(emailPath);
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                if (!file.exists()) {
                    try {
                        file.createNewFile();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                byte bytes[] = new byte[512];
                bytes = content.getBytes();
                int b = bytes.length;   // 是字节的长度，不是字符串的长度
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(bytes, 0, b);
                fos.close();

                // 第二次调用，显示进度
                caller.call(100);
            }
            System.out.println("==> Receive all and finished.");
            popFolder.close(true);
            store.close();
            finishCaller.call();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String mailReceiver(int index, Message msg) throws Exception {
        System.out.println("==> 正在接收第" + (index + 1) + "封邮件...");
        Address[] froms = msg.getFrom();
        if (froms != null) {
            InternetAddress addr = (InternetAddress) froms[0];
            parameter.put("a", addr.getAddress());
            parameter.put("b", addr.getPersonal());
            System.out.println("From: " + (addr.getPersonal() != null ? addr.getPersonal() : "") +
                    " ( " + addr.getAddress() + " )");
        }
        parameter.put("c", msg.getSubject());
        System.out.println("Title: " + msg.getSubject());
        Object o = msg.getContent();
        parameter.put("h", new SimpleDateFormat("yyyy-MM-dd").format(msg.getSentDate()));
        if (o instanceof Multipart) {
            Multipart multipart = (Multipart) o;
            reMultipart(multipart);
        } else if (o instanceof Part) {
            Part part = (Part) o;
        } else {
            System.out.println("Type: " + msg.getContentType());
            System.out.println("Content: " + msg.getContent());
            parameter.put("f", MimeUtility.decodeText(msg.getContentType()));
            parameter.put("d", msg.getContent());
        }
        if (parameter.get("a") == null) {
            parameter.put("a", "无");
        }
        if (parameter.get("b") == null) {
            parameter.put("b", "无");
        }
        if (parameter.get("c") == null) {
            parameter.put("c", "无");
        }
        if (parameter.get("d") == null) {
            parameter.put("d", "无");
        }
        if (parameter.get("e") == null) {
            parameter.put("e", "无");
        }
        if (parameter.get("f") == null) {
            parameter.put("f", "无");
        }
        if (parameter.get("g") == null) {
            parameter.put("g", "无");
        }

        String str = AnyChartUtil.getAnyChartXml("emails.ftl", parameter);
        return str;
    }

    private Map<String, Object> rePart(Part part)
            throws MessagingException, UnsupportedEncodingException, IOException, FileNotFoundException {
        if (part.getDisposition() != null) {
            String partName = part.getFileName();
            if (partName == null) partName = "unnamed file";

            String strFileName = MimeUtility.decodeText(partName);
            parameter.put("e", MimeUtility.decodeText(partName));
            parameter.put("f", MimeUtility.decodeText(part.getContentType()));
            //parameter.put("g", part.getContent());
            System.out.println("发现附件: " + MimeUtility.decodeText(partName));
            System.out.println("附件类型: " + MimeUtility.decodeText(part.getContentType()));
            System.out.println("附件内容: " + part.getContent());
            String projectPath = System.getProperty("user.dir");
            String type = strFileName.substring(strFileName.lastIndexOf(".") + 1);
            if (type.equals("zip")) {
                String fileName = projectPath + "/resources/temp/" + strFileName;
                InputStream in = part.getInputStream();
                FileOutputStream out = new FileOutputStream(fileName);
                int data;
                while ((data = in.read()) != -1) {
                    out.write(data);
                }
                in.close();
                out.close();
                parameter.put("g", "<a href='" + fileName + "'>" + strFileName + "</a>");
                String unzipPath = projectPath + "/resources/downloadFile/" + strFileName;
                try {
                    IG_FileUtil.deCompress(fileName, unzipPath);
                    IG_FileUtil.delete(fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (type.equals("rar")) {
                String fileName = projectPath + "/resources/temp/" + strFileName;
                InputStream in = part.getInputStream();
                FileOutputStream out = new FileOutputStream(fileName);
                int data;
                while ((data = in.read()) != -1) {
                    out.write(data);
                }
                in.close();
                out.close();
                parameter.put("g", "<a href='" + fileName + "'>" + strFileName + "</a>");
                String unzipPath = projectPath + "/resources/downloadFile/" + strFileName;
                try {
                    IG_FileUtil.deCompress(fileName, unzipPath);
                    IG_FileUtil.delete(fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                String fileName = projectPath + "/resources/downloadFile/" + strFileName;
                InputStream in = part.getInputStream();
                FileOutputStream out = new FileOutputStream(fileName);
                int data;
                while ((data = in.read()) != -1) {
                    out.write(data);
                }
                in.close();
                out.close();
                parameter.put("g", "<a href='" + fileName + "'>" + strFileName + "</a>");
            }
        } else {
            if (part.getContentType().startsWith("text/plain")) {
                parameter.put("d", part.getContent());
                System.out.println(part.getContent());
            } else {
                parameter.put("d", part.getContent());
//                System.out.println("HTML内容：\n" + part.getContent());
            }
        }
        return parameter;
    }

    private void reMultipart(Multipart multipart) throws Exception {
        for (int j = 0, n = multipart.getCount(); j < n; j++) {
            Part part = multipart.getBodyPart(j);
            if (part.getContent() instanceof Multipart) {
                Multipart p = (Multipart) part.getContent();
                reMultipart(p);

            } else {
                rePart(part);
            }
        }
    }

}

class EmailAuthenticator extends Authenticator {
    private String username = null;
    private String password = null;

    public EmailAuthenticator(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password);
    }
}
